//GA+SHC common
//$Id: ga_shc_common.h 719 2012-06-06 17:30:15Z Oleg.Bulychov@gmail.com $
#pragma once

#include "src/heo/include/statistics.h"
#include "solvers/shc/shc_common.h"
#include "solvers/ga/ga_common.h"

namespace ga
{

namespace inter_operator
{

template<class E, class T>
class shc:
    public factory_object< shc<E, T>, abstract_operator<E, T> >
{
    typedef SHC_Traits<E>   shc_traits;
    SHC<shc_traits>*        shc_;
    int                     np_;
    double                  probability_;
public:
    explicit shc(): shc_(NULL), np_(0)
    {}

    ~shc()
    {
        delete [] shc_;
    }

    static char const* get_Name() { return SHC<shc_traits>::get_Name(); }
    static char const* get_Section() { return get_Name(); }

    template<class V>
    void accept(V& v)
    {
        v(VAR_(probability),   0.01);
        for (int i = 0; i < np_; ++i)
            v(shc_[i], "/");//:?
    }

    virtual void init(E* env)
    {
        if (shc_)
            return;
        np_ = 1;
#if defined(_OPENMP)
        np_ = omp_get_max_threads();
#endif
        shc_ = new SHC<shc_traits>[np_];
        accept(meta::set_default_values());
    }

    virtual void apply(T** list, int size,
        E* env,
        int rank,
        int thread_id,
        int step,
        int max_step,
        random_generator& rnd)
    {
        SHC<shc_traits>& shc = *shc_;
        shc.init(env, rank, thread_id, true);
        Statistics dummy_stat;
        for (int i = 0; i < size; ++i)
        {
            if (rnd.next_Double() < probability_)
            {
                list[i]->evaluate(env, rnd);
                shc.solve(*list[i], dummy_stat);
            }
        }
    }
#if defined(_OPENMP)
    virtual void apply_OMP(T** list, int size,
        E* env,
        int mpi_rank,
        int step,
        int max_step)
    {
#pragma omp parallel
        {
            int rank = omp_get_thread_num();
            SHC<shc_traits>& shc = shc_[rank];
            int np = omp_get_num_threads();
            shc.init(env, mpi_rank, rank, true);
            random_generator rnd(rank);
            Statistics dummy_stat;
            for (int i = rank; i < size; i += np)
            {
                if (rnd.next_Double() < probability_)
                {
                    list[i]->evaluate(env, rnd);
                    shc.solve(*list[i], dummy_stat);
                }
            }
        }
//:#pragma omp barrier
    }
#endif
};

}//:inter_operator

}//:ga


struct validator_ga_shc_tag
{};

template<class E, class T>
struct validator_ga<E, T, validator_ga_shc_tag>: public meta::abstract_validator
{
    template<class U>
    void set_Default(std::vector< ref_ptr<U> >& v) const
    {
        v.clear();
        ref_ptr<U> pt;
        using namespace ga::inter_operator;
        {
            shc<E, T>* px = new shc<E, T>;
            pt.attach(px);
            v.push_back(pt);
        }
        {
            crossover<E, T>* px = new crossover<E, T>;
            pt.attach(px);
            v.push_back(pt);
        }
        {
            mutation<E, T>* px = new mutation<E, T>;
            pt.attach(px);
            v.push_back(pt);
        }
    }
};


//EOF!
